<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
import TreeStore from './types/tree-store'
import TreeNode from './types/node'
import type { TreeNodeData, TreeKey, TreeOptionProps } from './types/tree'
import Node from './components/node.vue'

defineOptions({ name: 'Tree' })

interface Props {
  data?: TreeNodeData[]
  height?: number | string
  itemHeight?: number
  defaultExpandAll?: boolean
  highlightCurrent?: boolean
  showCheckbox?: boolean
  checkStrictly?: boolean
  defaultCheckedKeys?: TreeKey[]
  defaultExpandedKeys?: TreeKey[]
  nodeKey?: string
  props?: TreeOptionProps
  accordion?: boolean
  indent?: number
}

const props = withDefaults(defineProps<Props>(), {
  data: () => [],
  height: 400,
  itemHeight: 32,
  defaultExpandAll: true,
  highlightCurrent: true,
  showCheckbox: false,
  checkStrictly: false,
  defaultCheckedKeys: () => [],
  defaultExpandedKeys: () => [],
  nodeKey: 'id',
  props: () => ({
    children: 'children',
    label: 'label',
  }),
  accordion: false,
  indent: 20,
})

const emit = defineEmits<{
  nodeClick: [data: TreeNodeData, node: TreeNode]
  nodeExpand: [data: TreeNodeData, node: TreeNode]
  nodeCollapse: [data: TreeNodeData, node: TreeNode]
  check: [data: TreeNodeData, checked: any]
  currentChange: [data: TreeNodeData | null, node: TreeNode | null]
}>()

// 容器引用
const containerRef = ref<HTMLElement | null>(null)
const scrollTop = ref(0)
const viewportHeight = ref(0)

// TreeStore 实例
const store = ref<TreeStore | null>(null)

// 扁平化的可见节点列表（用于虚拟滚动）
const flattenedNodes = ref<TreeNode[]>([])

// 初始化 TreeStore
const initStore = () => {
  store.value = new TreeStore({
    key: props.nodeKey,
    data: props.data,
    lazy: false,
    props: props.props,
    currentNodeKey: undefined,
    checkStrictly: props.checkStrictly,
    checkDescendants: false,
    defaultCheckedKeys: props.defaultCheckedKeys,
    defaultExpandedKeys: props.defaultExpandedKeys,
    autoExpandParent: true,
    defaultExpandAll: props.defaultExpandAll,
    filterNodeMethod: () => true,
  } as any)

  store.value.initialize()
  updateFlattenedNodes()
}

// 扁平化树节点（只包含可见的节点）
const updateFlattenedNodes = () => {
  if (!store.value) return

  const nodes: TreeNode[] = []
  const traverse = (nodeList: TreeNode[]) => {
    for (const node of nodeList) {
      if (node.visible) {
        nodes.push(node)
        if (node.expanded && node.childNodes.length > 0) {
          traverse(node.childNodes)
        }
      }
    }
  }

  traverse(store.value.root.childNodes)
  flattenedNodes.value = nodes
}

// 可见的节点（虚拟滚动）
const visibleNodes = computed(() => {
  if (!containerRef.value) return []
  if (flattenedNodes.value.length === 0) return []

  const start = Math.floor(scrollTop.value / props.itemHeight)
  const end = Math.min(
    flattenedNodes.value.length,
    start + Math.ceil(viewportHeight.value / props.itemHeight) + 2
  )

  return flattenedNodes.value.slice(Math.max(0, start - 1), end + 1)
})

// 内容偏移量
const contentOffset = computed(() => {
  const start = Math.floor(scrollTop.value / props.itemHeight)
  return Math.max(0, start - 1) * props.itemHeight
})

// 总高度
const totalHeight = computed(() => {
  return flattenedNodes.value.length * props.itemHeight
})

// 滚动处理
const onScroll = (e: Event) => {
  const target = e.target as HTMLElement
  scrollTop.value = target.scrollTop
}

// 节点点击
const handleNodeClick = (data: TreeNodeData, node: TreeNode) => {
  emit('nodeClick', data, node)
  if (props.highlightCurrent) {
    store.value?.setCurrentNode(node)
    emit('currentChange', data, node)
  }
}

// 节点展开
const handleNodeExpand = (data: TreeNodeData, node: TreeNode) => {
  node.expand()
  updateFlattenedNodes()
  emit('nodeExpand', data, node)
}

// 节点折叠
const handleNodeCollapse = (data: TreeNodeData, node: TreeNode) => {
  node.collapse()
  updateFlattenedNodes()
  emit('nodeCollapse', data, node)
}

// 复选框变化
const handleCheck = (data: TreeNodeData, checked: any) => {
  emit('check', data, checked)
}

// 监听数据变化
watch(
  () => props.data,
  () => {
    initStore()
  },
  { immediate: true, deep: true }
)

// 监听展开状态变化
watch(
  () => flattenedNodes.value.length,
  () => {
    // 展开/折叠后更新可见节点
  }
)

// 组件挂载
onMounted(() => {
  if (containerRef.value) {
    viewportHeight.value = containerRef.value.clientHeight
  }
  initStore()
})
</script>

<template>
  <div
    ref="containerRef"
    class="tree"
    :style="{ height: typeof height === 'number' ? `${height}px` : height }"
    @scroll="onScroll"
  >
    <div class="tree__phantom" :style="{ height: `${totalHeight}px` }">
      <div
        class="tree__content"
        :style="{ transform: `translateY(${contentOffset}px)` }"
      >
        <Node
          v-for="node in visibleNodes"
          :key="node.id"
          :node="node"
          :item-height="itemHeight"
          :indent="indent"
          :show-checkbox="showCheckbox"
          :highlight-current="highlightCurrent"
          :current-node="store?.currentNode"
          @node-click="handleNodeClick"
          @node-expand="handleNodeExpand"
          @node-collapse="handleNodeCollapse"
          @check="handleCheck"
        />
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.tree {
  width: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  position: relative;
  -webkit-overflow-scrolling: touch;
  background: var(--el-bg-color);
  padding: 4px 0;

  // 自定义滚动条样式
  &::-webkit-scrollbar {
    width: 6px;
  }

  &::-webkit-scrollbar-track {
    background: transparent;
    border-radius: 3px;
  }

  &::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.2);
    border-radius: 3px;
    transition: background 0.3s;

    &:hover {
      background: rgba(0, 0, 0, 0.3);
    }
  }
}

.tree__phantom {
  position: relative;
  width: 100%;
}

.tree__content {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  transition: transform 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}
</style>
